/*
解构不仅可以用于数组，还可以用于对象。说白了数组的索引就是对象的key
但是对象的解构与数组有一个重要的不同。数组的元素是按次序排列的，变量的取值由它的位置决定；
而对象的属性没有次序，变量必须与属性同名，才能取到正确的值。
如果解构失败，变量的值等于undefined
*/
let {
	foo,
	bar
} = {
	foo: 'aaa',
	bar: 'bbb'
};
foo // "aaa"
bar // "bbb"

let {
	baz
} = {
	foo: 'aaa',
	bar: 'bbb'
};
baz // undefined



/*
对象的解构赋值,可以很方便地将现有对象的方法， 赋值到某个变量。
下面代码的例一将Math对象的对数、 正弦、 余弦三个方法， 赋值到对应的变量上， 使用起来就会方便很多。
例二将console.log赋值到log变量。
*/
// 例一
let {
	log,
	sin,
	cos
} = Math;

// 例二
const {
	log
} = console;
log('hello') // hello



/*
如果变量名与属性名不一致，必须写成下面这样。 其实这也是原理所在
也就是说，对象的解构赋值的内部机制，是先找到同名属性，然后再赋给对应的变量。真正被赋值的是后者，而不是前者。
*/
let {
	foo: baz
} = {
	foo: 'aaa',
	bar: 'bbb'
};
baz // "aaa"

let obj = {
	first: 'hello',
	last: 'world'
};
let {
	first: f,
	last: l
} = obj;
f // 'hello'
l // 'world'



//与数组一样， 解构也可以用于嵌套结构的对象。
let obj = {
	p: [
		'Hello',
		{
			y: 'World'
		}
	]
};

let {
	p: [x, {
		y
	}]
} = obj;
x // "Hello"
y // "World"



// 这也是对象解构的特点,模式匹配,哪怕右边只有一个,只要左边模式对上了就可以进行匹配,不需要像数组一样遵循顺序
let obj = {
	p: [
		'Hello',
		{
			y: 'World'
		}
	]
};

let {
	p,
	p: [x, {
		y
	}]
} = obj;
x // "Hello"
y // "World"
p // ["Hello", {y: "World"}]
const node = {
	loc: {
		start: {
			line: 1,
			column: 5
		}
	}
};

let {
	loc,
	loc: {
		start
	},
	loc: {
		start: {
			line
		}
	}
} = node;
line // 1
loc // Object {start: Object}
start // Object {line: 1, column: 5}


//嵌套赋值
let obj = {};
let arr = [];

({
	foo: obj.prop,
	bar: arr[0]
} = {
	foo: 123,
	bar: true
});

obj // {prop:123}
arr // [true]


//注意，对象的解构赋值可以取到继承的属性。
const obj1 = {};
const obj2 = {
	foo: 'bar'
};
Object.setPrototypeOf(obj1, obj2);

const {
	foo
} = obj1; //因为obj1的_proto_里有foo
foo // "bar"


/*
 ******************************默认值
 */

//对象的解构也可以指定默认值。
var {
	x = 3
} = {};
x // 3

var {
	x,
	y = 5
} = {
	x: 1
};
x // 1
y // 5

var {
	x: y = 3
} = {};
y // 3  再次体现左边的只是模式  等价于 let [y=3] =[]

var {
	x: y = 3
} = {
	x: 5
};
y // 5

var {
	message: msg = 'Something went wrong'
} = {};
msg // "Something went wrong"

//默认值生效的条件是，对象的属性值严格等于undefined。这一点和数组默认值规则一样
var {
	x = 3
} = {
	x: undefined
};
x // 3

var {
	x = 3
} = {
	x: null
};
x // null

/*
如果要将一个已经声明的变量用于解构赋值，必须非常小心。
下面代码的写法会报错，因为 JavaScript 引擎会将{x}理解成一个代码块，从而发生语法错误。
只有不将大括号写在行首，避免 JavaScript 将其解释为代码块，才能解决这个问题。
*/

// 错误的写法
let x; {
	x
} = {
	x: 1
};
// SyntaxError: syntax error

// 正确的写法
let x;
({
	x
} = {
	x: 1
});


/*
解构赋值允许等号左边的模式之中， 不放置任何变量名。 因此， 可以写出非常古怪的赋值表达式。
下面的表达式虽然毫无意义， 但是语法是合法的， 可以执行。
 */
({} = [true, false]);
({} = 'abc');
({} = []);

/*
由于数组本质是特殊的对象，因此可以对数组进行对象属性的解构。
下面代码对数组进行对象解构。数组arr的0键对应的值是1，[arr.length - 1]就是2键，对应的值是3。
*/

let arr = [1, 2, 3];
let {
	0: first,
	[arr.length - 1]: last
} = arr;
first // 1
last // 3


